﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace WindowsFormsApplication1
{
    class Triangle3D
    {
        public Vector4 A, B, C;
        private Vector4 a, b, c;        //为什么要什么这个三个成员，是因为我们要保持原顶点数据不发生改变
        private float dot;              //法线和平面的点积值
        private bool cullBack = false;  //背面剔除，即背面不画


        public Triangle3D() { }
        public Triangle3D(Vector4 a, Vector4 b, Vector4 c)
        {
            this.A = this.a = new Vector4(a);
            this.B = this.b = new Vector4(b);
            this.C = this.c = new Vector4(c);
        }

        /// <summary>
        /// 三角形利用矩阵的乘法进行变换
        /// </summary>
        /// <param name="m"></param>
        public void Transform(Matrix4x4 m)
        {
            this.a = m.Mul(this.A);
            this.b = m.Mul(this.B);
            this.c = m.Mul(this.C);
        }

        /// <summary>
        /// 三角形面的法向量
        /// </summary>
        /// <returns></returns>
        public void CalculateLighting(Matrix4x4 _object2World, Vector4 l)
        {
            this.Transform(_object2World); //从模型到世界
            Vector4 u = this.b - this.a;
            Vector4 v = this.c - this.a;
            Vector4 normal = u.Cross(v); //叉乘得到法向量
            //法向量和光向量的规范化之后点积
            dot = normal.Normalized.Dot(l.Normalized);
            dot = Math.Max(0, dot);

            Vector4 E = new Vector4(0, 0, -1, 0); //z轴的负方向
            cullBack = normal.Normalized.Dot(E) < 0 ? true : false;
        }

        /// <summary>
        /// 画三角形到2D窗口
        /// </summary>
        /// <param name="g"></param>
        public void Draw(Graphics g)
        {
            g.TranslateTransform(200, 200); //左上角为(0,0)，这里将坐标原点移动到(100,100)处。
            //边框渲染
            g.DrawLines(new Pen(Color.Red, 2), this.Get2DPointFArr());
            if(!cullBack) //背面剔除
            {
                GraphicsPath path = new GraphicsPath();
                path.AddLines(this.Get2DPointFArr());
                //int r = (int)(255 * dot); //太黑
                int r = (int)(200 * dot) + 55;
                Color color = Color.FromArgb(r, r, r);
                Brush br = new SolidBrush(color);
                g.FillPath(br, path);
            }
        }

        /// <summary>
        /// 需要四个顶点，才能是封闭的三角形。这是DrawLines函数的要求。
        /// </summary>
        /// <returns></returns>
        private PointF[] Get2DPointFArr()
        {
            PointF[] arr = new PointF[4];
            arr[0] = Get2DPointF(this.a);
            arr[1] = Get2DPointF(this.b);
            arr[2] = Get2DPointF(this.c);
            arr[3] = arr[0];
            return arr;
        }

        /// <summary>
        /// 透视除法，得到二维屏幕坐标的点的坐标，这一步操作，还没有映射到cvv空间，只是得到相似三角形的上的点。
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private PointF Get2DPointF(Vector4 v)
        {
            PointF p = new PointF();
            p.X = (float)(v.x / v.w); 
            p.Y = (float)(v.y / v.w);
            return p;
        }
    }
}
